<?php declare(strict_types=1);


namespace App\Utils\Wechat\OpenPlatform;


use App\Constants\ErrorCode;
use App\Exception\BusinessException;
use App\Utils\Log;
use App\Utils\Wechat\Base;
use EasyWeChat\Kernel\Exceptions\BadRequestException;
use EasyWeChat\Kernel\Exceptions\HttpException;
use EasyWeChat\Kernel\Exceptions\InvalidArgumentException;
use EasyWeChat\Kernel\Exceptions\InvalidConfigException;
use EasyWeChat\Kernel\Exceptions\RuntimeException;
use EasyWeChat\Kernel\Support\Collection;
use EasyWeChat\OpenPlatform\Application;
use EasyWeChat\OpenPlatform\Auth\VerifyTicket;
use Exception;
use GuzzleHttp\Exception\GuzzleException;
use Hyperf\Redis\Redis;
use Psr\Http\Message\ResponseInterface;
use Symfony\Component\HttpFoundation\Response;

class OpenPlatform extends Base
{
    private $lifeTimeKey = 'c:easywechat.kernel.access_token.lifetime';



    public function __construct()
    {
        $this->config = config('wechat.open_platform');
        $this->app = make(Application::class, [$this->config]);
        parent::__construct();
    }

    /**
     * @return Application|mixed
     */
    public function getApp()
    {
        return $this->app;
    }

    /**
     * 接收微信开放平台传递的 ticket ，并缓存到 Redis
     * @return Response
     * @throws InvalidArgumentException
     * @throws BadRequestException
     * @throws InvalidConfigException
     */
    public function setTicket()
    {
        $get = $this->request->getQueryParams();
        $post = $this->request->getParsedBody();
        $cookie = $this->request->getCookieParams();
        $files = $this->request->getUploadedFiles();
        $server = $this->request->getServerParams();
        $xml = $this->request->getBody()->getContents();

        $this->app['request'] = new \Symfony\Component\HttpFoundation\Request($get,$post,[],$cookie,$files,$server,$xml);

        return $this->app->server->serve();
    }

    /**
     * 获取 ticket
     * @return bool|string
     */
    public function getTicket()
    {
        try {
            return make(VerifyTicket::class, [$this->app])->getTicket();
        } catch (InvalidArgumentException $e) {
        } catch (RuntimeException $e) {
        } catch (\Psr\SimpleCache\InvalidArgumentException $e) {
        }
        return false;
    }

    /**
     * 获取 access_token
     * @return mixed
     * @throws HttpException
     * @throws InvalidArgumentException
     * @throws InvalidConfigException
     * @throws RuntimeException
     * @throws \Psr\SimpleCache\InvalidArgumentException
     */
    public function accessToken()
    {
        $redis = di(Redis::class);

        // 判断是否需要刷新 access_token
        if (!$redis->get($this->lifeTimeKey)) {
            $refresh = true;
        } else {
            $refresh = false;
        }
        $token = $this->app->access_token->getToken($refresh);

        // 获取成功，记录下一次需要重新刷新 access_token 的时间
        if (isset($token['component_access_token']) && !is_null($token['component_access_token'])) {
            $redis->set($this->lifeTimeKey, '1', 6600);
            return $token['component_access_token'];
        }
        return false;
    }

    /**
     * 生产授权页面URL
     * @param string $callbackUrl
     * @return string
     */
    public function preAuthUrl(string $callbackUrl): string
    {
        return $this->app->getPreAuthorizationUrl($callbackUrl);
    }

    /**
     * 用户授权后跳转到，回调页面，并且携带auth_code。使用auth_code获取用户的授权信息：重点获取（authorizer_access_token）
     * @param $authCode
     * @return mixed
     * @throws Exception
     */
    public function handleAuthorize(string $authCode): array
    {
        try {
            $authorizer = $this->app->handleAuthorize($authCode);
            if (!isset($authorizer['authorization_info']['authorizer_appid'])) {
                throw new RuntimeException('WeChat open platform returns null data');
            }
            return (array)$authorizer;
        } catch (\Throwable $e) {
            throw new Exception($e->getMessage(), $e->getCode());
        }
    }

    /**
     * 获取已授权的小程序或公众号基本信息和授权信息
     * @param string $appID
     * @return mixed
     */
    public function authInfo(string $appID)
    {
        if ($this->redis->hExists('authorizer', $appID)) {
            $res = $this->redis->hGet('authorizer', $appID);
            return json_decode($res, true);
        }
        $res = $this->app->getAuthorizer($appID);
        if (isset($res['authorizer_info']) && isset($res['authorization_info'])) {
            $this->redis->hSet('authorizer', $appID, json_encode($res, JSON_UNESCAPED_UNICODE));
        } else {
            Log::get()->info(json_encode($res, JSON_UNESCAPED_UNICODE));
            throw new BusinessException(ErrorCode::WX_AUTHORIZATION_FAIL);
        }
        return $res;
    }

    /**
     * 获取微信小程序或公众号的基本信息
     * @param string $appID
     * @return mixed
     */
    public function authorizer(string $appID)
    {
        return $this->authInfo($appID)['authorizer_info'];
    }

    /**
     * 获取微信小程序或微信公众号的授权信息
     * @param string $appID
     * @return mixed
     */
    public function authorization(string $appID)
    {
        return $this->authInfo($appID)['authorization_info'];
    }

    /**
     * 获取刷新令牌，可用于后面重新获取
     * @param string $appID
     * @return mixed
     */
    public function authorizerRefreshToken(string $appID)
    {
        return $this->authorization($appID)['authorizer_refresh_token'];
    }


    /**
     * 获取模板列表
     * @return mixed
     * @throws InvalidConfigException
     * @throws GuzzleException
     */
    public function templateList()
    {
//        if ($this->redis->exists(\App\Constants\Redis\OpenPlatform::TEMPLATE_LIST)) {
//            return json_decode($this->redis->get(\App\Constants\Redis\OpenPlatform::TEMPLATE_LIST), true);
//        }
        $res = $this->app->code_template->list();
        if ($res['errcode'] == 0&&strtolower($res['errmsg']) == 'ok') {
//            $this->redis->set(\App\Constants\Redis\OpenPlatform::TEMPLATE_LIST, json_encode($res['template_list']), 1800);
            $res = $res['template_list'];
        }
        return $res;
    }
}